Introduction

This is code for Section 17.2 of Kruschke using a Student t noise distribution. Other than that change, this document is very similar to the one from section 7.1.

Make sure that the two files “HtWtData30.csv” and “HtWtData300.csv” are in your project directory.

Preliminaries

Load necessary packages:

library(tidyverse)
library(rstan)
library(tidybayes)
library(bayesplot)

Set Stan to save compiled code.

rstan_options(auto_write = TRUE)

Set Stan to use parallel processing where possible.

options(mc.cores = parallel::detectCores())

Data

HtWtData30 <- read_csv("HtWtData30.csv")
Rows: 30 Columns: 3── Column specification ─────────────────────────────────────────
Delimiter: ","
dbl (3): male, height, weight
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
HtWtData30
ggplot(HtWtData30, aes(y = weight, x = height)) +
    geom_point()

HtWtData300 <- read_csv("HtWtData300.csv")
Rows: 300 Columns: 3── Column specification ─────────────────────────────────────────
Delimiter: ","
dbl (3): male, height, weight
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
HtWtData300
ggplot(HtWtData300, aes(y = weight, x = height)) +
    geom_point()

Mean center the x variable. (Kruschke standardizes both variables using z-scores, but that makes the coefficients of the model harder to interpret.)

HtWtData30 <- HtWtData30 %>%
    mutate(height_mc = height - mean(height))
HtWtData30
ggplot(HtWtData30, aes(y = weight, x = height_mc)) +
    geom_point()

HtWtData300 <- HtWtData300 %>%
    mutate(height_mc = height - mean(height))
HtWtData300
ggplot(HtWtData300, aes(y = weight, x = height_mc)) +
    geom_point()

We’re going to run two different examples, so we’ll make two lists.

stan_data_30 <- list(N = NROW(HtWtData30),
                     y = HtWtData30$weight,
                     x = HtWtData30$height_mc)
stan_data_300 <- list(N = NROW(HtWtData300),
                      y = HtWtData300$weight,
                      x = HtWtData300$height_mc)

Prior predictive check

Simulation code

data {
    int<lower = 0> N;
    vector<lower = 0>[N] y;
    vector[N] x;
}
generated quantities {
    real nu;
    real beta0;
    real beta1;
    vector[N] mu;
    real<lower = 0> sigma;
    real y_sim[N];
    
    nu = gamma_rng(2, 0.1);       // default prior for df
    beta0 = normal_rng(150, 25);  // Intercept between 100 and 200
    beta1 = normal_rng(0, 5);    // -10 to 10 lbs per inch
    mu = beta0 + beta1 * x;       // linear model
    sigma = exponential_rng(0.1); // default prior for sigma
    y_sim = student_t_rng(nu, mu, sigma);
}

Simulate data

fit_HtWt30_robust_prior <- sampling(HtWt_robust_prior,
                                    data = stan_data_30,
                                    chains = 1,
                                    algorithm = "Fixed_param",
                                    seed = 11111,
                                    refresh = 0)

Extract samples

samples_HtWt30_prior <- tidy_draws(fit_HtWt30_robust_prior)
samples_HtWt30_prior
y_sim_prior <- samples_HtWt30_prior %>%
    select(starts_with("y_sim")) %>%
    as.matrix()

Plot results

ggplot(HtWtData30, aes(y = weight, x = height_mc)) +
    geom_point() +
    geom_abline(data = samples_HtWt30_prior,
                aes(intercept = beta0, slope = beta1),
                alpha = 0.05)

mcmc_hist(samples_HtWt30_prior,
          pars = c("nu", "sigma", "beta0", "beta1"))

ppc_intervals(y = HtWtData30$weight,
              x = HtWtData30$height,
              yrep = y_sim_prior)

Model

data {
    int<lower = 0> N;
    vector<lower = 0>[N] y;
    vector[N] x;
}
parameters {
    real nu;
    real beta0;
    real beta1;
    real<lower = 0> sigma;
}
transformed parameters {
    vector[N] mu;
    mu = beta0 + beta1 * x;   // linear model
}
model {
    nu ~ gamma(2, 0.1);       // default prior for df
    beta0 ~ normal(150, 25);  // Intercept between 100 and 200
    beta1 ~ normal(0, 5);    // -10 to 10 lbs per inch
    sigma ~ exponential(0.1); // default prior for sigma
    y ~ student_t(nu, mu, sigma);
}
generated quantities {
    real y_sim[N];
    y_sim = student_t_rng(nu, mu, sigma);
}
fit_HtWt30_robust <- sampling(HtWt_robust,
                              data = stan_data_30,
                              seed = 11111,
                              refresh = 0)
Warning: There were 676 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.Warning: Examine the pairs() plot to diagnose sampling problems
Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
Running the chains for more iterations may help. See
https://mc-stan.org/misc/warnings.html#tail-ess

Diagnosing the model

plot(fit_HtWt30_robust, plotfun = "ac",
     pars = c("nu", "beta0", "beta1", "sigma"))

plot(fit_HtWt30_robust, plotfun = "trace",
     pars = c("nu", "beta0", "beta1", "sigma"))

Summarizing the model

print(fit_HtWt30_robust, pars = c("nu", "beta0", "beta1", "sigma"))
Inference for Stan model: anon_model.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

        mean se_mean    sd   2.5%    25%    50%    75%  97.5%
nu     16.17    0.49 11.92   2.95   7.17  12.86  21.76  47.73
beta0 152.19    0.13  4.72 143.17 149.05 152.09 155.14 161.87
beta1   4.31    0.04  1.21   1.85   3.53   4.33   5.12   6.55
sigma  24.05    0.19  4.19  16.65  21.08  23.79  26.67  32.84
      n_eff Rhat
nu      584    1
beta0  1279    1
beta1  1191    1
sigma   481    1

Samples were drawn using NUTS(diag_e) at Fri May 12 12:08:46 2023.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

Visualizing the model

pairs(fit_HtWt30_robust,
           pars = c("nu", "beta0", "beta1", "sigma"))

stan_dens(fit_HtWt30_robust,
     pars = c("nu", "beta0", "beta1", "sigma"))

plot(fit_HtWt30_robust, pars = c("nu", "beta0", "beta1", "sigma"))
ci_level: 0.8 (80% intervals)
outer_level: 0.95 (95% intervals)

The model with 300 observations

fit_HtWt300_robust <- sampling(HtWt_robust,
                               data = stan_data_300,
                               seed = 11111,
                               refresh = 0)
Warning: There were 1 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.Warning: Examine the pairs() plot to diagnose sampling problems
plot(fit_HtWt300_robust, plotfun = "ac",
     pars = c("nu", "beta0", "beta1", "sigma"))

plot(fit_HtWt300_robust, plotfun = "trace",
     pars = c("nu", "beta0", "beta1", "sigma"))

print(fit_HtWt300_robust,
      pars = c("nu", "beta0", "beta1", "sigma"))
Inference for Stan model: anon_model.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

        mean se_mean   sd   2.5%    25%    50%    75%  97.5%
nu      5.59    0.05 1.79   3.27   4.34   5.25   6.39  10.11
beta0 156.24    0.03 1.67 153.03 155.10 156.22 157.36 159.52
beta1   4.43    0.01 0.41   3.60   4.15   4.43   4.70   5.22
sigma  23.95    0.04 1.66  20.84  22.77  23.92  25.01  27.46
      n_eff Rhat
nu     1142    1
beta0  3146    1
beta1  3097    1
sigma  1656    1

Samples were drawn using NUTS(diag_e) at Fri May 12 12:18:30 2023.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).
pairs(fit_HtWt300_robust,
      pars = c("nu", "beta0", "beta1", "sigma"))

plot(fit_HtWt300_robust, plotfun = "dens",
     pars = c("nu", "beta0", "beta1", "sigma"))

plot(fit_HtWt300_robust,
     pars = c("nu", "beta0", "beta1", "sigma"))
ci_level: 0.8 (80% intervals)
outer_level: 0.95 (95% intervals)

Posterior predictive check

We’ll just do this for the example with 30 observations.

Extract samples

samples_HtWt30 <- tidy_draws(fit_HtWt30_robust)
samples_HtWt30
y_sim_post <- samples_HtWt30 %>%
    select(starts_with("y_sim")) %>%
    as.matrix()

Plot results

ggplot(HtWtData30, aes(y = weight, x = height_mc)) +
    geom_point() +
    geom_abline(data = samples_HtWt30,
                aes(intercept = beta0, slope = beta1),
                alpha = 0.01) +
    geom_abline(data = samples_HtWt30,
                aes(intercept = mean(beta0), slope = mean(beta1)),
                color = "blue", size = 2)

Compare to standard linear regression.

ggplot(HtWtData30, aes(y = weight, x = height_mc)) +
    geom_point() +
    geom_abline(data = samples_HtWt30,
                aes(intercept = beta0, slope = beta1),
                alpha = 0.01) +
    geom_abline(data = samples_HtWt30,
                aes(intercept = mean(beta0), slope = mean(beta1)),
                color = "blue", size = 2) +
    geom_smooth(method = lm, color = "red", size = 2)

ppc_intervals(y = HtWtData30$weight,
              x = HtWtData30$height,
              yrep = y_sim_post)

ppc_hist(y, y_sim_post[1:5, ])

ppc_boxplot(y, y_sim[1:5, ])

ppc_dens(y, y_sim[1:5, ])

ppc_dens_overlay(y, y_sim[1:30, ])

ppc_stat_2d(y, y_sim)

LS0tDQp0aXRsZTogIjE3LjI6IFJvYnVzdCByZWdyZXNzaW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBpcyBjb2RlIGZvciBTZWN0aW9uIDE3LjIgb2YgS3J1c2Noa2UgdXNpbmcgYSBTdHVkZW50IHQgbm9pc2UgZGlzdHJpYnV0aW9uLiBPdGhlciB0aGFuIHRoYXQgY2hhbmdlLCB0aGlzIGRvY3VtZW50IGlzIHZlcnkgc2ltaWxhciB0byB0aGUgb25lIGZyb20gc2VjdGlvbiA3LjEuDQoNCk1ha2Ugc3VyZSB0aGF0IHRoZSB0d28gZmlsZXMgIkh0V3REYXRhMzAuY3N2IiBhbmQgIkh0V3REYXRhMzAwLmNzdiIgYXJlIGluIHlvdXIgcHJvamVjdCBkaXJlY3RvcnkuDQoNCg0KIyMgUHJlbGltaW5hcmllcw0KDQpMb2FkIG5lY2Vzc2FyeSBwYWNrYWdlczoNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocnN0YW4pDQpsaWJyYXJ5KHRpZHliYXllcykNCmxpYnJhcnkoYmF5ZXNwbG90KQ0KYGBgDQoNClNldCBTdGFuIHRvIHNhdmUgY29tcGlsZWQgY29kZS4NCg0KYGBge3J9DQpyc3Rhbl9vcHRpb25zKGF1dG9fd3JpdGUgPSBUUlVFKQ0KYGBgDQoNClNldCBTdGFuIHRvIHVzZSBwYXJhbGxlbCBwcm9jZXNzaW5nIHdoZXJlIHBvc3NpYmxlLg0KDQpgYGB7cn0NCm9wdGlvbnMobWMuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSkNCmBgYA0KDQoNCiMjIERhdGENCg0KYGBge3J9DQpIdFd0RGF0YTMwIDwtIHJlYWRfY3N2KCJIdFd0RGF0YTMwLmNzdiIpDQpIdFd0RGF0YTMwDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoSHRXdERhdGEzMCwgYWVzKHkgPSB3ZWlnaHQsIHggPSBoZWlnaHQpKSArDQogICAgZ2VvbV9wb2ludCgpDQpgYGANCg0KYGBge3J9DQpIdFd0RGF0YTMwMCA8LSByZWFkX2NzdigiSHRXdERhdGEzMDAuY3N2IikNCkh0V3REYXRhMzAwDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoSHRXdERhdGEzMDAsIGFlcyh5ID0gd2VpZ2h0LCB4ID0gaGVpZ2h0KSkgKw0KICAgIGdlb21fcG9pbnQoKQ0KYGBgDQoNCg0KTWVhbiBjZW50ZXIgdGhlIHggdmFyaWFibGUuIChLcnVzY2hrZSBzdGFuZGFyZGl6ZXMgYm90aCB2YXJpYWJsZXMgdXNpbmcgei1zY29yZXMsIGJ1dCB0aGF0IG1ha2VzIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIG1vZGVsIGhhcmRlciB0byBpbnRlcnByZXQuKQ0KDQpgYGB7cn0NCkh0V3REYXRhMzAgPC0gSHRXdERhdGEzMCAlPiUNCiAgICBtdXRhdGUoaGVpZ2h0X21jID0gaGVpZ2h0IC0gbWVhbihoZWlnaHQpKQ0KSHRXdERhdGEzMA0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KEh0V3REYXRhMzAsIGFlcyh5ID0gd2VpZ2h0LCB4ID0gaGVpZ2h0X21jKSkgKw0KICAgIGdlb21fcG9pbnQoKQ0KYGBgDQoNCg0KYGBge3J9DQpIdFd0RGF0YTMwMCA8LSBIdFd0RGF0YTMwMCAlPiUNCiAgICBtdXRhdGUoaGVpZ2h0X21jID0gaGVpZ2h0IC0gbWVhbihoZWlnaHQpKQ0KSHRXdERhdGEzMDANCmBgYA0KDQpgYGB7cn0NCmdncGxvdChIdFd0RGF0YTMwMCwgYWVzKHkgPSB3ZWlnaHQsIHggPSBoZWlnaHRfbWMpKSArDQogICAgZ2VvbV9wb2ludCgpDQpgYGANCg0KV2UncmUgZ29pbmcgdG8gcnVuIHR3byBkaWZmZXJlbnQgZXhhbXBsZXMsIHNvIHdlJ2xsIG1ha2UgdHdvIGxpc3RzLg0KDQpgYGB7cn0NCnN0YW5fZGF0YV8zMCA8LSBsaXN0KE4gPSBOUk9XKEh0V3REYXRhMzApLA0KICAgICAgICAgICAgICAgICAgICAgeSA9IEh0V3REYXRhMzAkd2VpZ2h0LA0KICAgICAgICAgICAgICAgICAgICAgeCA9IEh0V3REYXRhMzAkaGVpZ2h0X21jKQ0Kc3Rhbl9kYXRhXzMwMCA8LSBsaXN0KE4gPSBOUk9XKEh0V3REYXRhMzAwKSwNCiAgICAgICAgICAgICAgICAgICAgICB5ID0gSHRXdERhdGEzMDAkd2VpZ2h0LA0KICAgICAgICAgICAgICAgICAgICAgIHggPSBIdFd0RGF0YTMwMCRoZWlnaHRfbWMpDQpgYGANCg0KDQojIyBQcmlvciBwcmVkaWN0aXZlIGNoZWNrDQoNCiMjIyBTaW11bGF0aW9uIGNvZGUNCg0KYGBge3N0YW4sIG91dHB1dC52YXIgPSAiSHRXdF9yb2J1c3RfcHJpb3IiLCBjYWNoZSA9IFRSVUV9DQpkYXRhIHsNCiAgICBpbnQ8bG93ZXIgPSAwPiBOOw0KICAgIHZlY3Rvcjxsb3dlciA9IDA+W05dIHk7DQogICAgdmVjdG9yW05dIHg7DQp9DQpnZW5lcmF0ZWQgcXVhbnRpdGllcyB7DQogICAgcmVhbCBudTsNCiAgICByZWFsIGJldGEwOw0KICAgIHJlYWwgYmV0YTE7DQogICAgdmVjdG9yW05dIG11Ow0KICAgIHJlYWw8bG93ZXIgPSAwPiBzaWdtYTsNCiAgICByZWFsIHlfc2ltW05dOw0KICAgIA0KICAgIG51ID0gZ2FtbWFfcm5nKDIsIDAuMSk7ICAgICAgIC8vIGRlZmF1bHQgcHJpb3IgZm9yIGRmDQogICAgYmV0YTAgPSBub3JtYWxfcm5nKDE1MCwgMjUpOyAgLy8gSW50ZXJjZXB0IGJldHdlZW4gMTAwIGFuZCAyMDANCiAgICBiZXRhMSA9IG5vcm1hbF9ybmcoMCwgNSk7ICAgIC8vIC0xMCB0byAxMCBsYnMgcGVyIGluY2gNCiAgICBtdSA9IGJldGEwICsgYmV0YTEgKiB4OyAgICAgICAvLyBsaW5lYXIgbW9kZWwNCiAgICBzaWdtYSA9IGV4cG9uZW50aWFsX3JuZygwLjEpOyAvLyBkZWZhdWx0IHByaW9yIGZvciBzaWdtYQ0KICAgIHlfc2ltID0gc3R1ZGVudF90X3JuZyhudSwgbXUsIHNpZ21hKTsNCn0NCmBgYA0KDQojIyMgU2ltdWxhdGUgZGF0YQ0KDQpgYGB7cn0NCmZpdF9IdFd0MzBfcm9idXN0X3ByaW9yIDwtIHNhbXBsaW5nKEh0V3Rfcm9idXN0X3ByaW9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHN0YW5fZGF0YV8zMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGdvcml0aG0gPSAiRml4ZWRfcGFyYW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDExMTExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmcmVzaCA9IDApDQpgYGANCg0KIyMjIEV4dHJhY3Qgc2FtcGxlcw0KDQpgYGB7cn0NCnNhbXBsZXNfSHRXdDMwX3ByaW9yIDwtIHRpZHlfZHJhd3MoZml0X0h0V3QzMF9yb2J1c3RfcHJpb3IpDQpzYW1wbGVzX0h0V3QzMF9wcmlvcg0KYGBgDQoNCmBgYHtyfQ0KeV9zaW1fcHJpb3IgPC0gc2FtcGxlc19IdFd0MzBfcHJpb3IgJT4lDQogICAgc2VsZWN0KHN0YXJ0c193aXRoKCJ5X3NpbSIpKSAlPiUNCiAgICBhcy5tYXRyaXgoKQ0KYGBgDQoNCiMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3J9DQpnZ3Bsb3QoSHRXdERhdGEzMCwgYWVzKHkgPSB3ZWlnaHQsIHggPSBoZWlnaHRfbWMpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX2FibGluZShkYXRhID0gc2FtcGxlc19IdFd0MzBfcHJpb3IsDQogICAgICAgICAgICAgICAgYWVzKGludGVyY2VwdCA9IGJldGEwLCBzbG9wZSA9IGJldGExKSwNCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUpDQpgYGANCg0KYGBge3J9DQptY21jX2hpc3Qoc2FtcGxlc19IdFd0MzBfcHJpb3IsDQogICAgICAgICAgcGFycyA9IGMoIm51IiwgInNpZ21hIiwgImJldGEwIiwgImJldGExIikpDQpgYGANCg0KDQoNCmBgYHtyfQ0KcHBjX2ludGVydmFscyh5ID0gSHRXdERhdGEzMCR3ZWlnaHQsDQogICAgICAgICAgICAgIHggPSBIdFd0RGF0YTMwJGhlaWdodCwNCiAgICAgICAgICAgICAgeXJlcCA9IHlfc2ltX3ByaW9yKQ0KYGBgDQoNCg0KIyMgTW9kZWwNCg0KYGBge3N0YW4sIG91dHB1dC52YXIgPSAiSHRXdF9yb2J1c3QiLCBjYWNoZSA9IFRSVUV9DQpkYXRhIHsNCiAgICBpbnQ8bG93ZXIgPSAwPiBOOw0KICAgIHZlY3Rvcjxsb3dlciA9IDA+W05dIHk7DQogICAgdmVjdG9yW05dIHg7DQp9DQpwYXJhbWV0ZXJzIHsNCiAgICByZWFsIG51Ow0KICAgIHJlYWwgYmV0YTA7DQogICAgcmVhbCBiZXRhMTsNCiAgICByZWFsPGxvd2VyID0gMD4gc2lnbWE7DQp9DQp0cmFuc2Zvcm1lZCBwYXJhbWV0ZXJzIHsNCiAgICB2ZWN0b3JbTl0gbXU7DQogICAgbXUgPSBiZXRhMCArIGJldGExICogeDsgICAvLyBsaW5lYXIgbW9kZWwNCn0NCm1vZGVsIHsNCiAgICBudSB+IGdhbW1hKDIsIDAuMSk7ICAgICAgIC8vIGRlZmF1bHQgcHJpb3IgZm9yIGRmDQogICAgYmV0YTAgfiBub3JtYWwoMTUwLCAyNSk7ICAvLyBJbnRlcmNlcHQgYmV0d2VlbiAxMDAgYW5kIDIwMA0KICAgIGJldGExIH4gbm9ybWFsKDAsIDUpOyAgICAvLyAtMTAgdG8gMTAgbGJzIHBlciBpbmNoDQogICAgc2lnbWEgfiBleHBvbmVudGlhbCgwLjEpOyAvLyBkZWZhdWx0IHByaW9yIGZvciBzaWdtYQ0KICAgIHkgfiBzdHVkZW50X3QobnUsIG11LCBzaWdtYSk7DQp9DQpnZW5lcmF0ZWQgcXVhbnRpdGllcyB7DQogICAgcmVhbCB5X3NpbVtOXTsNCiAgICB5X3NpbSA9IHN0dWRlbnRfdF9ybmcobnUsIG11LCBzaWdtYSk7DQp9DQpgYGANCg0KYGBge3J9DQpmaXRfSHRXdDMwX3JvYnVzdCA8LSBzYW1wbGluZyhIdFd0X3JvYnVzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzdGFuX2RhdGFfMzAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMTExMTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZyZXNoID0gMCkNCmBgYA0KDQoNCiMjIERpYWdub3NpbmcgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KcGxvdChmaXRfSHRXdDMwX3JvYnVzdCwgcGxvdGZ1biA9ICJhYyIsDQogICAgIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChmaXRfSHRXdDMwX3JvYnVzdCwgcGxvdGZ1biA9ICJ0cmFjZSIsDQogICAgIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCg0KIyMgU3VtbWFyaXppbmcgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KcHJpbnQoZml0X0h0V3QzMF9yb2J1c3QsIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCg0KIyMgVmlzdWFsaXppbmcgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KcGFpcnMoZml0X0h0V3QzMF9yb2J1c3QsDQogICAgICBwYXJzID0gYygibnUiLCAiYmV0YTAiLCAiYmV0YTEiLCAic2lnbWEiKSkNCmBgYA0KDQpgYGB7cn0NCnN0YW5fZGVucyhmaXRfSHRXdDMwX3JvYnVzdCwNCiAgICAgcGFycyA9IGMoIm51IiwgImJldGEwIiwgImJldGExIiwgInNpZ21hIikpDQpgYGANCg0KYGBge3J9DQpwbG90KGZpdF9IdFd0MzBfcm9idXN0LA0KICAgICBwYXJzID0gYygibnUiLCAiYmV0YTAiLCAiYmV0YTEiLCAic2lnbWEiKSkNCmBgYA0KDQoNCiMjIFRoZSBtb2RlbCB3aXRoIDMwMCBvYnNlcnZhdGlvbnMNCg0KYGBge3J9DQpmaXRfSHRXdDMwMF9yb2J1c3QgPC0gc2FtcGxpbmcoSHRXdF9yb2J1c3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHN0YW5fZGF0YV8zMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDExMTExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZnJlc2ggPSAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChmaXRfSHRXdDMwMF9yb2J1c3QsIHBsb3RmdW4gPSAiYWMiLA0KICAgICBwYXJzID0gYygibnUiLCAiYmV0YTAiLCAiYmV0YTEiLCAic2lnbWEiKSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoZml0X0h0V3QzMDBfcm9idXN0LCBwbG90ZnVuID0gInRyYWNlIiwNCiAgICAgcGFycyA9IGMoIm51IiwgImJldGEwIiwgImJldGExIiwgInNpZ21hIikpDQpgYGANCg0KYGBge3J9DQpwcmludChmaXRfSHRXdDMwMF9yb2J1c3QsDQogICAgICBwYXJzID0gYygibnUiLCAiYmV0YTAiLCAiYmV0YTEiLCAic2lnbWEiKSkNCmBgYA0KDQpgYGB7cn0NCnBhaXJzKGZpdF9IdFd0MzAwX3JvYnVzdCwNCiAgICAgIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChmaXRfSHRXdDMwMF9yb2J1c3QsIHBsb3RmdW4gPSAiZGVucyIsDQogICAgIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChmaXRfSHRXdDMwMF9yb2J1c3QsDQogICAgIHBhcnMgPSBjKCJudSIsICJiZXRhMCIsICJiZXRhMSIsICJzaWdtYSIpKQ0KYGBgDQoNCg0KIyMgUG9zdGVyaW9yIHByZWRpY3RpdmUgY2hlY2sNCg0KV2UnbGwganVzdCBkbyB0aGlzIGZvciB0aGUgZXhhbXBsZSB3aXRoIDMwIG9ic2VydmF0aW9ucy4NCg0KIyMjIEV4dHJhY3Qgc2FtcGxlcw0KDQpgYGB7cn0NCnNhbXBsZXNfSHRXdDMwIDwtIHRpZHlfZHJhd3MoZml0X0h0V3QzMF9yb2J1c3QpDQpzYW1wbGVzX0h0V3QzMA0KYGBgDQoNCmBgYHtyfQ0KeV9zaW1fcG9zdCA8LSBzYW1wbGVzX0h0V3QzMCAlPiUNCiAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoInlfc2ltIikpICU+JQ0KICAgIGFzLm1hdHJpeCgpDQpgYGANCg0KIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7cn0NCmdncGxvdChIdFd0RGF0YTMwLCBhZXMoeSA9IHdlaWdodCwgeCA9IGhlaWdodF9tYykpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fYWJsaW5lKGRhdGEgPSBzYW1wbGVzX0h0V3QzMCwNCiAgICAgICAgICAgICAgICBhZXMoaW50ZXJjZXB0ID0gYmV0YTAsIHNsb3BlID0gYmV0YTEpLA0KICAgICAgICAgICAgICAgIGFscGhhID0gMC4wMSkgKw0KICAgIGdlb21fYWJsaW5lKGRhdGEgPSBzYW1wbGVzX0h0V3QzMCwNCiAgICAgICAgICAgICAgICBhZXMoaW50ZXJjZXB0ID0gbWVhbihiZXRhMCksIHNsb3BlID0gbWVhbihiZXRhMSkpLA0KICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLCBzaXplID0gMikNCmBgYA0KDQpDb21wYXJlIHRvIHN0YW5kYXJkIGxpbmVhciByZWdyZXNzaW9uLg0KDQpgYGB7cn0NCmdncGxvdChIdFd0RGF0YTMwLCBhZXMoeSA9IHdlaWdodCwgeCA9IGhlaWdodF9tYykpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fYWJsaW5lKGRhdGEgPSBzYW1wbGVzX0h0V3QzMCwNCiAgICAgICAgICAgICAgICBhZXMoaW50ZXJjZXB0ID0gYmV0YTAsIHNsb3BlID0gYmV0YTEpLA0KICAgICAgICAgICAgICAgIGFscGhhID0gMC4wMSkgKw0KICAgIGdlb21fYWJsaW5lKGRhdGEgPSBzYW1wbGVzX0h0V3QzMCwNCiAgICAgICAgICAgICAgICBhZXMoaW50ZXJjZXB0ID0gbWVhbihiZXRhMCksIHNsb3BlID0gbWVhbihiZXRhMSkpLA0KICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLCBzaXplID0gMikgKw0KICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMikNCmBgYA0KDQpgYGB7cn0NCnBwY19pbnRlcnZhbHMoeSA9IEh0V3REYXRhMzAkd2VpZ2h0LA0KICAgICAgICAgICAgICB4ID0gSHRXdERhdGEzMCRoZWlnaHQsDQogICAgICAgICAgICAgIHlyZXAgPSB5X3NpbV9wb3N0KQ0KYGBgDQoNCmBgYHtyfQ0KcHBjX2hpc3QoeSwgeV9zaW1fcG9zdFsxOjUsIF0pDQpgYGANCg0KYGBge3J9DQpwcGNfYm94cGxvdCh5LCB5X3NpbVsxOjUsIF0pDQpgYGANCg0KYGBge3J9DQpwcGNfZGVucyh5LCB5X3NpbVsxOjUsIF0pDQpgYGANCg0KYGBge3J9DQpwcGNfZGVuc19vdmVybGF5KHksIHlfc2ltWzE6MzAsIF0pDQpgYGANCg0KYGBge3J9DQpwcGNfc3RhdF8yZCh5LCB5X3NpbSkNCmBgYA==